
#include "ElementoExterno.h"

ElementoExterno::ElementoExterno(int x,int y,int id,std::string ip, int port,Cliente* cliente, int cant_entradas,int cant_salidas,
							std::string name):ElementoCircuital(x,y,id){
	cant_conexiones_actuales=0;
	vueltas=0;
	simulado=false;

	//debo recuperar la informacion para poder definir (y luego dibujar) la caja negra
	this->ip=ip;
	this->port=port;
	this->tipo=TIPO_EXTERNO;
	this->cliente=cliente;
	this->nombre = name;
	int i;
	for(i=0;i<cant_entradas;i++)
		L_entradas.push_back(NULL);

	for(i=0;i<cant_salidas;i++)
		L_salidas.push_back(new SalidaElementoExterno());
}

ElementoExterno::~ElementoExterno() {
	liberar_entradas();
}

void ElementoExterno:: liberar_entradas(){
	std::list<Conexion*>::iterator lit(L_entradas.begin());
	std::list<Conexion*>::const_iterator lend(L_entradas.end());
	while(lit!=lend){
		if(*lit){
			((*lit)->get_emisor())->cortar_cable((*lit)->get_id());
		}
		lit++;
	}
}

void ElementoExterno::get_centro(int *x, int *y){
	int angulo = get_angulo();
	int altura;
	if (get_cant_entradas()>get_cant_salidas())
		altura = get_cant_entradas()*TAMANIO;
	else
		altura = get_cant_salidas()*TAMANIO;
	if ((angulo==90)||(angulo==270)){
		*x = get_posicion_x() + altura/2;
		*y = get_posicion_y() + TAMANIO;
	}
	else{
		*x = get_posicion_x() + TAMANIO;
		*y = get_posicion_y() + altura/2;
	}

}


int ElementoExterno::get_pos_x_repaint(){
	if ((get_angulo() == 0)||(get_angulo()==180)){
		return get_posicion_x();
	}
	else{
		int altura;
		if (get_cant_entradas()>get_cant_salidas())
			altura = get_cant_entradas()*TAMANIO;
		else
			altura = get_cant_salidas()*TAMANIO;
		return get_posicion_x()-TAMANIO+altura/2;
	}
}

int ElementoExterno::get_pos_y_repaint(){
	if ((get_angulo() == 0)||(get_angulo()==180)){
		return get_posicion_y();
	}
	else{
		int altura;
		if (get_cant_entradas()>get_cant_salidas())
			altura = get_cant_entradas()*TAMANIO;
		else
			altura = get_cant_salidas()*TAMANIO;
		return get_posicion_y()+TAMANIO-altura/2;
	}
}

int ElementoExterno::get_posicion_entrada_y(int nro){
	int resultado;
	if (get_angulo()==90){
		resultado = get_posicion_y() + 3;
	}
	if (get_angulo() == 270){
		resultado = get_posicion_y() + TAMANIO*2 - 3;
	}
	if (get_angulo()==0){
		if (get_cant_entradas()>get_cant_salidas()){
			resultado = get_posicion_y() + TAMANIO*nro - TAMANIO / 2;
		}
		else{
			int altura;
			altura = get_cant_salidas()*TAMANIO;
			int auxiliar = altura / get_cant_entradas();
			int distancia = auxiliar/2;
			resultado = get_posicion_y()+ auxiliar*nro - distancia;
		}
	}
	if (get_angulo()==180){

		if (get_cant_entradas()>get_cant_salidas()){
			resultado = get_posicion_y() + get_cant_entradas()*TAMANIO - TAMANIO*nro + TAMANIO / 2;
		}
		else{
			int altura;
			altura = get_cant_salidas()*TAMANIO;
			int auxiliar = altura / get_cant_entradas();
			int distancia = auxiliar/2;
			resultado = get_posicion_y() + altura - auxiliar*nro + distancia;
		}
	}
	return resultado;
}


int ElementoExterno::get_posicion_salida_y(int nro){
	int resultado;
	if (get_angulo()==90){
		resultado = get_posicion_y() + TAMANIO*2 - 3;
	}
	if (get_angulo() == 270){
		resultado = get_posicion_y() + 3;
	}
	if (get_angulo()==0){
		if (get_cant_salidas()>get_cant_entradas()){
			resultado = get_posicion_y() + TAMANIO*nro - TAMANIO / 2;
		}
		else{
			int altura;
			altura = get_cant_entradas()*TAMANIO;
			int auxiliar = altura / get_cant_salidas();
			int distancia = auxiliar/2;
			resultado = get_posicion_y()+ auxiliar*nro - distancia;
		}
	}
	if (get_angulo()==180){
		if (get_cant_salidas()>get_cant_entradas()){
			resultado = get_posicion_y() + get_cant_salidas()*TAMANIO - TAMANIO*nro + TAMANIO / 2;
		}
		else{
			int altura;
			altura = get_cant_entradas()*TAMANIO;
			int auxiliar = altura / get_cant_salidas();
			int distancia = auxiliar/2;
			resultado = get_posicion_y() + altura - auxiliar*nro + distancia;
		}
	}
	return resultado;
}

int ElementoExterno::get_nro_entrada(int x, int y){
	int cantidad_entradas = get_cant_entradas();
	int coordenada;
	int posicion;
	if ((get_angulo()==0)||(get_angulo()==180)){
		posicion = get_posicion_y();
		coordenada = y;
	}
	else{
		posicion = get_posicion_x();
		coordenada = x;
	}
	int nro_entrada = 0;
	if (get_cant_salidas()<get_cant_entradas()){
		int i;
		bool encontrado = false;
		for (i=0; ((i<cantidad_entradas)&&(!encontrado)); i++){

			if ((coordenada>posicion)&&(coordenada<posicion+TAMANIO)){
				encontrado = true;

				if ((get_angulo()==180)||(get_angulo()==270)){
					nro_entrada = cantidad_entradas-i;
				}
				else
					nro_entrada = i+1;
			}
			posicion+=TAMANIO;
		}
	}
	else{
		int altura = get_cant_salidas()*TAMANIO;
		int auxiliar = altura / get_cant_entradas();
		int i;
		bool encontrado = false;
		for (i=0; ((i<cantidad_entradas)&&(!encontrado)); i++){
			if ((coordenada>posicion)&&(coordenada<posicion+auxiliar)){
				encontrado = true;
				if ((get_angulo()==180)||(get_angulo()==270)){
					nro_entrada = cantidad_entradas-i;
				}
				else
					nro_entrada = i+1;
			}
			posicion+=auxiliar;
		}
	}
	return nro_entrada;
}

int ElementoExterno::get_nro_salida(int x, int y){

	int cantidad_salidas = get_cant_salidas();
	int coordenada;
	int posicion;
	if ((get_angulo()==0)||(get_angulo()==180)){
		posicion = get_posicion_y();
		coordenada = y;
	}
	else{
		posicion = get_posicion_x();
		coordenada = x;
	}
	int nro_salida = 1;
	if (get_cant_entradas()<get_cant_salidas()){
		int i;
		bool encontrado = false;
		for (i=0; ((i<cantidad_salidas)&&(!encontrado)); i++){
			if ((coordenada>posicion)&&(coordenada<posicion+TAMANIO)){
				encontrado = true;
				if ((get_angulo()==180)||(get_angulo()==270)){
					nro_salida = cantidad_salidas-i;
				}
				else
					nro_salida = i+1;
			}
			posicion+=TAMANIO;
		}
	}
	else{
		int altura = get_cant_entradas()*TAMANIO;
		int auxiliar = altura / get_cant_salidas();
		int i;
		bool encontrado = false;
		for (i=0; ((i<cantidad_salidas)&&(!encontrado)); i++){

			if ((coordenada>posicion)&&(coordenada<posicion+auxiliar)){
				encontrado = true;
				if ((get_angulo()==180)||(get_angulo()==270)){
					nro_salida = cantidad_salidas-i;
				}
				else
					nro_salida = i+1;
			}
			posicion+=auxiliar;
		}
	}
	return nro_salida;
}

int ElementoExterno::get_posicion_entrada_x (int nro){
	int resultado;
	if (get_angulo()==0){
		resultado = get_posicion_x() + 3;
	}
	if (get_angulo() == 180){
		resultado = get_posicion_x() + TAMANIO*2 - 3;
	}
	if (get_angulo()==90){
		if (get_cant_entradas()>get_cant_salidas()){
			resultado = get_posicion_x() + TAMANIO*nro - TAMANIO / 2;
		}
		else{
			int altura;
			altura = get_cant_salidas()*TAMANIO;
			int auxiliar = altura / get_cant_entradas();
			int distancia = auxiliar/2;
			resultado = get_posicion_x()+ auxiliar*nro - distancia;
		}
	}
	if (get_angulo()==270){

		if (get_cant_entradas()>get_cant_salidas()){
			resultado = get_posicion_x() + get_cant_entradas()*TAMANIO - TAMANIO*nro + TAMANIO / 2;
		}
		else{
			int altura;
			altura = get_cant_salidas()*TAMANIO;
			int auxiliar = altura / get_cant_entradas();
			int distancia = auxiliar/2;
			resultado = get_posicion_x() + altura - auxiliar*nro + distancia;
		}
	}
	return resultado;
}

int ElementoExterno::get_posicion_salida_x(int nro){
	int resultado;
	if (get_angulo()==0)
		resultado = get_posicion_x() + TAMANIO*2 - 3;
	if (get_angulo() == 180)
		resultado = get_posicion_x() + 3;
	if (get_angulo()==90){
		if (get_cant_salidas()>get_cant_entradas()){
			resultado = get_posicion_x() + TAMANIO*nro - TAMANIO / 2;
		}
		else{
			int altura;
			altura = get_cant_entradas()*TAMANIO;
			int auxiliar = altura / get_cant_salidas();
			int distancia = auxiliar/2;
			resultado = get_posicion_x()+ auxiliar*nro - distancia;
		}
	}
	if (get_angulo()==270){
		if (get_cant_salidas()>get_cant_entradas()){
			resultado = get_posicion_x() + get_cant_salidas()*TAMANIO - TAMANIO*nro + TAMANIO / 2;
		}
		else{
			int altura;
			altura = get_cant_entradas()*TAMANIO;
			int auxiliar = altura / get_cant_salidas();
			int distancia = auxiliar/2;
			resultado = get_posicion_x() + altura - auxiliar*nro + distancia;
		}
	}
	return resultado;
}


bool ElementoExterno:: calcular_salida(int nro_salida,int& t_transicion){
	/*
	 * >for recorro toda la lista de entradas pidiendo la salida de cada una su salida
	 * >lleno mi lista de valores de entrada
	 * >llamo al cliente obtener simulacion con la lista de entradas y la de salidas
	 * >devuelvo la posicion de la lista de salidas correspondiente al nro_salida
	 */

	if(!this->ready())
		throw NoSimulacionExcepcion();

	if(!simulado){
		simulado  = true;
		cant_conexiones_actuales=calcular_cant_conexiones();
		std::list<Conexion*>::iterator lit(L_entradas.begin());
		std::list<Conexion*>::iterator litend(L_entradas.end());
		entradas="";
		while(lit!=litend){
			if((*lit)->get_emisor()->calcular_salida((*lit)->get_nro_salida(),t_transicion))
				entradas+="1";
			else
				entradas+="0";
			lit++;
		}
		salidas=cliente->obtener_salida(ip,port,nombre,entradas);
		if(salidas == "ERROR")
			throw NoSimulacionExcepcion();
	}

	//parsea la salida para devolver lo pedido
	int i=1, aux=0;
	while(i<nro_salida){
		while(salidas[aux]!='|')
			aux++;
		i++;
	}

	if(salidas[aux]=='|')
		aux++;

	std::string salida_aux;
	salida_aux+=salidas[aux];
	int salida=atoi(salida_aux.c_str());
	aux++;
	std::string auxiliar;
	while(salidas[aux]!='|'){
		auxiliar+=salidas[aux];
		aux++;
	}
	t_transicion+=atoi(auxiliar.c_str());

	vueltas++;
	if(vueltas==cant_conexiones_actuales){
		simulado=false;
		vueltas=0;
		cant_conexiones_actuales=0;
	}
	return salida;
}

int ElementoExterno::calcular_cant_conexiones(){
	int i=0;
	std::list<SalidaElementoExterno*>::iterator lit_salidas(L_salidas.begin());
	std::list<SalidaElementoExterno*>::const_iterator lend_salidas(L_salidas.end());
	while(lit_salidas!=lend_salidas){
		std::list<Conexion*>::iterator lit((*lit_salidas)->L_conexiones.begin());
		std::list<Conexion*>::const_iterator lend((*lit_salidas)->L_conexiones.end());

		if(!(*lit_salidas)->L_conexiones.empty()){
			while(lit!=lend){
				i++;
				lit++;
			}
		}
		lit_salidas++;
	}
	return i;
}

void ElementoExterno:: desconectar_entrada(int nro){
	std::list<Conexion*>::iterator lit(L_entradas.begin());
	int i;
	for(i=1; i<nro;i++){
		lit++;
	}
	*lit=NULL;
}

void ElementoExterno::set_entrada(Conexion* cable){
	int nro=cable->get_nro_entrada();
	std::list<Conexion*>::iterator lit(L_entradas.begin());
	for(int i=1; i<nro;i++){
		lit++;
	}
	*lit=cable;
	if(cable->get_emisor() != NULL)
		(cable->get_emisor())->agregar_conexion(cable, cable->get_nro_salida());
}

void ElementoExterno::recalcular_dimension(){
	int altura;
	if (get_cant_entradas()>get_cant_salidas())
		altura = get_cant_entradas()*TAMANIO;
	else
		altura = get_cant_salidas()*TAMANIO;
	int angulo_anterior;
	if (get_angulo()==0)
		angulo_anterior = 270;
	else
		angulo_anterior = get_angulo()-90;
	int x;
	int y;
	if ((angulo_anterior == 0)||(angulo_anterior==180)){
		x = get_posicion_x() + TAMANIO - altura/2;
		y = get_posicion_y() + altura/2 - TAMANIO;
	}
	else {
		x = get_posicion_x() + altura/2 - TAMANIO;
		y = get_posicion_y() + TAMANIO - altura/2;
	}
	set_posicionX(x);
	set_posicionY(y);

}


void ElementoExterno::agregar_conexion(Conexion* nueva_conexion, int nro_salida){

	std::list<SalidaElementoExterno*>::iterator lit_salidas(L_salidas.begin());
	int i;
	for(i=1; i<nro_salida;i++){
		lit_salidas++;
	}

	(*lit_salidas)->L_conexiones.push_back(nueva_conexion);

}

bool ElementoExterno::ready(){
	bool listo=true;
	std::list<Conexion*>::iterator lit(L_entradas.begin());
	std::list<Conexion*>::iterator litend(L_entradas.end());

	while ((lit!=litend) && (listo)){
		if(*lit==NULL)
			listo=false;
		lit++;
	}
	return listo;
}

void ElementoExterno:: liberar_conexiones(){
	if(L_salidas.empty()){
		return ;
	}
	else{
		std::list<SalidaElementoExterno*>::iterator lit_salidas(L_salidas.begin());
		std::list<SalidaElementoExterno*>::const_iterator lend_salidas(L_salidas.end());
		while(lit_salidas!=lend_salidas){
			std::list<Conexion*>::iterator lit((*lit_salidas)->L_conexiones.begin());
			std::list<Conexion*>::const_iterator lend((*lit_salidas)->L_conexiones.end());

			if(!(*lit_salidas)->L_conexiones.empty()){
				while(lit!=lend){
					((*lit)->get_receptor())->desconectar_entrada((*lit)->get_nro_entrada());
					delete *lit;
					lit++;
				}
				(*lit_salidas)->L_conexiones.clear();
			}
			else{}
			lit_salidas++;
		}
	}
}

void ElementoExterno::cortar_cable(int id_cable){
	if(L_salidas.empty())
		return ;
	else{
		std::list<SalidaElementoExterno*>::iterator lit_salidas(L_salidas.begin());
		std::list<SalidaElementoExterno*>::const_iterator lend_salidas(L_salidas.end());
		bool encontrado=false;

		while((lit_salidas!=lend_salidas)&&(!encontrado)){
			std::list<Conexion*>::iterator lit((*lit_salidas)->L_conexiones.begin());
			std::list<Conexion*>::const_iterator lend((*lit_salidas)->L_conexiones.end());

			if(!(*lit_salidas)->L_conexiones.empty()){
				while((lit!=lend)&&(!encontrado)){
					if((*lit)->get_id()==id_cable){
						((*lit)->get_receptor())->desconectar_entrada((*lit)->get_nro_entrada());
						encontrado=true;
					}
					else
						lit++;
				}
				if(encontrado){
						delete *lit;
						(*lit_salidas)->L_conexiones.erase(lit);
				}
			}
			lit_salidas++;
		}
	}
}



bool ElementoExterno:: entrada_ocupada(int nro_entrada){
	std::list<Conexion*>::iterator lit(L_entradas.begin());
	for(int i=1;i<nro_entrada;i++){
		lit++;
	}
	if(*lit)
		return true;
	else
		return false;
}


void ElementoExterno::calcular_ancho(int *ancho, int *alto){
	if ((get_angulo()==0)||(get_angulo()==180)){
		*ancho = TAMANIO*2;
		if (get_cant_entradas()>get_cant_salidas()){
			*alto = TAMANIO*get_cant_entradas();
		}
		else{
			*alto = TAMANIO*get_cant_salidas();
		}
	}
	if ((get_angulo()==90)||(get_angulo()==270)){
		*alto = TAMANIO*2;
		if (get_cant_entradas()>get_cant_salidas()){
			*ancho = TAMANIO*get_cant_entradas();
		}
		else{
			*ancho = TAMANIO*get_cant_salidas();
		}
	}
}

